(function ($) {
	AjaxSolr.GraphWidget = AjaxSolr.AbstractWidget.extend({
		force:null,
		svg:null,
		interactions:null,
		
		init: function () {
			var self =this;
			$(this.target).empty();
			
		},
		afterRequest: function () {
			var self =this;
			$(this.target).empty();

			var width = $(this.target).width(),
			height = 500,
			nodes = [],
			r=6,
			links = [];
			
			var nodeA = Array(); // Associative array  [id]->Node
			self.interactions = Array(); 
			var proteins = this._getProteinsFromQuery();

			$("#info").html("Recovered: "+this.manager.response.response.docs.length+"/"+this.manager.response.response.numFound);
			for (var i = 0, l = this.manager.response.response.docs.length; i < l; i++) {
				var doc = this.manager.response.response.docs[i];
				var n1=0,n2=0;
				if (typeof nodeA[doc.protein1] == "undefined"){
					n1 = nodes.push({
						"name":doc.protein1,
						"group":this._getGroup(doc, proteins),
						"queried":false}) -1;
					nodeA[doc.protein1]=n1;
				}else
					n1 = nodeA[doc.protein1];

				if (typeof nodeA[doc.protein2] == "undefined"){
					n2 = nodes.push({
						"name":doc.protein2,
						"group":this._getGroup(doc, proteins),
						"queried":false}) -1;
					nodeA[doc.protein2]=n2;
				}else
					n2 = nodeA[doc.protein2];
				
				links.push({source: n1, target: n2,score:doc["unified-score"],doc:doc});
				
				
				if (typeof self.interactions[doc.protein1] == "undefined")
					self.interactions[doc.protein1]=[nodes[n2]];
				else
					self.interactions[doc.protein1].push(nodes[n2]);
				if (typeof self.interactions[doc.protein2] == "undefined")
					self.interactions[doc.protein2]=[nodes[n1]];
				else
					self.interactions[doc.protein2].push(nodes[n1]);
			}
			for (var i = 0, l = proteins.length; i < l; i++) {
				nodes[nodeA[proteins[i]]].group=i;
			}

			nodes[0].fixed=true;
			nodes[0].x = width/2;
			nodes[0].y = height/2;

			var color = d3.scale.category20();
			var force = d3.layout.force()
			.charge(-40)
			.linkDistance(20)
			.size([width, height]);

			self.force=force;
			
			var svg = d3.select(this.target).append("svg")
			.attr("width", width)
			.attr("height", height);

			self.svg=svg;
			
			force
			.nodes(nodes)
			.links(links)
			.start();
			
			var node_drag = d3.behavior.drag()
			.on("dragstart", dragstart)
			.on("drag", dragmove)
			.on("dragend", dragend);

			function dragstart(d, i) {
				force.stop(); // stops the force auto positioning before you start dragging
			}

			function dragmove(d, i) {
				d.px += d3.event.dx;
				d.py += d3.event.dy;
				d.x += d3.event.dx;
				d.y += d3.event.dy; 
				tick(); // this is the key to make it work together with updating both px,py,x,y on d !
			}

			function dragend(d, i) {
				d.fixed = true; // of course set the node to fixed so the force doesn't include the node in its auto positioning stuff
				tick();
				force.resume();
			}
			
			var link = svg.selectAll("line.link")
			.data(links)
			.enter().append("line")
			.attr("id", function(d) { return "link_"+d.source.name+"_"+d.target.name; })
			.attr("class",function(d1,d2) { return (d1.source.queried && d1.target.queried)?"linkqueried":"link"; })//, "link")
			.attr("doc", function(d) { return d.doc; })
			.style("stroke-width", function(d) { return Math.sqrt(d.value); });

			var node = svg.selectAll("circle.node")
			.data(nodes)
			.enter().append("circle")
			.attr("class",function(d) { return d.queried?"queried":"node";       })
			.attr("r", r - .75)
			.style("fill", function(d) {       	return color(d.group);       })
			.call(node_drag)
			.attr("id", function(d) { return "node_"+d.name; })
			.on("click", function(d){
				var value = d.name;
				var q=self.manager.store.get('q').val();
				q = (q=="*:*")?'text:'+value:q+' OR text:'+value;
				if (value && self.manager.store.addByValue('q', q)) {
					self.manager.doRequest(0);
				}
				$("#info").html(d.name);
			});

			node.append("title")
			.text(function(d) { return d.name; });
			force.on("tick", tick);
			function tick() {
				node.attr("cx", function(d) { return d.x = Math.max(r, Math.min(width - r, d.x)); })
					.attr("cy", function(d) { return d.y = Math.max(r, Math.min(height - r, d.y)); });
				link.attr("x1", function(d) { return d.source.x; })
					.attr("y1", function(d) { return d.source.y; })
					.attr("x2", function(d) { return d.target.x; })
					.attr("y2", function(d) { return d.target.y; });
			};
			self.applyRules();
		},	
		_getProteinsFromQuery: function(){
			var self=this;
			var q = self.manager.store.get('q').val();
			if (q=="*:*")
				return [];
			var proteins=q.split(" OR ");
			for (var i = 0, l = proteins.length; i < l; i++) {
				proteins[i]=proteins[i].substring(5);
			}
			return proteins;

		},
		_getGroup: function(doc, proteins){
			if (proteins.length<=1) 
				return 0;
			for (var i = 0, l = proteins.length; i < l; i++) {
				if (doc.protein1==proteins[i] || doc.protein2==proteins[i] )
					return i;
			}
			return 0;
		},
		applyRules: function(){
			var self =this;
			var rules = self.manager.widgets["ruler"].ruler.getActiveRules();
			var model = self.manager.widgets["ruler"].rules;
			
			//Reseting the graph 
			self.svg.selectAll("circle").attr("visibility", 'visible').attr("class", 'node').style("stroke","#fff");
			self.svg.selectAll("line").attr("visibility", 'visible').attr("class", 'link');
			
			var selector ="";
			for (var i=0;i<rules.length;i++){
				selector ="";
				var rule=rules[i];
				if (rule.target==model.target[0].name){ //Proteins
					switch (rule.condition){
						case model.target[0].conditions[0].name: // interactions with
							for (var j=0;j<self.interactions[rule.parameters[0]].length;j++){
								selector +="[id =node_"+self.interactions[rule.parameters[0]][j].name+"],";
							}
							selector = selector.substring(0, selector.length-1);
							break;
						case model.target[0].conditions[1].name: // accession number
							for (var interaction in self.interactions){
								switch (rule.parameters[0]){
									case "==":
										if (self.interactions[interaction].length==1*rule.parameters[1])
											selector +="[id=node_"+interaction+"],";
										break;
									case ">":
										if (self.interactions[interaction].length>1*rule.parameters[1])
											selector +="[id=node_"+interaction+"],";
										break;
									case "<":
										if (self.interactions[interaction].length<1*rule.parameters[1])
											selector +="[id=node_"+interaction+"],";
										break;
									case "<=":
										if (self.interactions[interaction].length<=1*rule.parameters[1])
											selector +="[id=node_"+interaction+"],";
										break;
									case ">=":
										if (self.interactions[interaction].length>=1*rule.parameters[1])
											selector +="[id=node_"+interaction+"],";
										break;
								}
							}
							if (selector.length>0) selector = selector.substring(0, selector.length-1);
							break;
						case model.target[0].conditions[2].name: // accession number
							switch (rule.parameters[0]){
								case "equals":
									selector ="#node_"+rule.parameters[1];
									break;
								case "contains":
									selector ="circle[id *="+rule.parameters[1]+"]";
									break;
								case "different":
									selector ='circle:not([id="node_'+rule.parameters[1]+'"])';
									break;
								case "not contains":
									selector ="circle:not([id *="+rule.parameters[1]+"])";
									break;
							}
							break;
					}
				} else if (rule.target==model.target[1].name) { //Interactions
					switch (rule.condition){
						case model.target[1].conditions[0].name: // protein
							selector ="line[id *="+rule.parameters[0]+"]";
							break;
						case model.target[1].conditions[1].name: // proteins
							selector ="line[id *="+rule.parameters[0]+"][id *="+rule.parameters[1]+"]";
							break;
						case model.target[1].conditions[2].name: // score
							var links= self.force.links();
							for (var j in links){
								var link = links[j];
								var score=link.doc["unified-score"];
								switch (rule.parameters[0]){
									case "==":
										if (1*score==1*rule.parameters[1])
											selector +="[id=link_"+link.source.name+"_"+link.target.name+"],";
										break;
									case ">":
										if (1*score>1*rule.parameters[1])
											selector +="[id=link_"+link.source.name+"_"+link.target.name+"],";
										break;
									case "<":
										if (1*score<1*rule.parameters[1])
											selector +="[id=link_"+link.source.name+"_"+link.target.name+"],";
										break;
									case "<=":
										if (1*score<=1*rule.parameters[1])
											selector +="[id=link_"+link.source.name+"_"+link.target.name+"],";
										break;
									case ">=":
										if (1*score>=1*rule.parameters[1])
											selector +="[id=link_"+link.source.name+"_"+link.target.name+"],";
										break;
								}
							}
							if (selector.length>0) selector = selector.substring(0, selector.length-1);
							break;
						case model.target[1].conditions[3].name: // type of evidence
							var links= self.force.links();
							for (var j in links){
								var link = links[j];
								var score=link.doc[rule.parameters[0]];
								if (typeof score != "undefined" && score*1>0.0){
									selector +="[id=link_"+link.source.name+"_"+link.target.name+"],";
								}
							}
							if (selector.length>0) selector = selector.substring(0, selector.length-1);
							break;
					}					
				}
				if (selector!="") switch (rule.action){
					case "Hide":
						self._hide(self.svg.selectAll(selector));
						break;
					case "Show":
						self._show(self.svg.selectAll(selector));
						break;
					case "Highlight":
						self._highlight(self.svg.selectAll(selector));
						break;
					case "Color":
						self._color(self.svg.selectAll(selector),rule.color);
						break;
				}
				var affected = (selector=="")?0:self.svg.selectAll(selector)[0].length;
				self.manager.widgets["ruler"].ruler.setAffectedByRule(rule.id,affected);
			}
		},
		_hide: function(selection){
			selection.attr("visibility", 'hidden');
		},
		_show: function(selection){
			selection.attr("visibility", 'visible');
		},
		_highlight: function(selection){
			selection.attr("class", 'highlight');
		},
		_color: function(selection,color){
			selection.style("stroke", color);
		}
	});
})(jQuery);